/**
    package: learn-go
    filename: weiboRedPacket
    author: diogo@gmail.com
    time: 2023/3/8 14:36
**/
/**
* 微信抢红包 普通得 map 发生 竞争情况 所以需要使用 互斥 sync.Map
* 在大量得写 和 读得情况下会发生 竞争
*
 */
package main

import (
	"fmt"
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/mvc"
	"math/rand"
	"sync"
	"time"
)

// 红包列表

var packageList *sync.Map = new(sync.Map)

type lotteryController struct {
	Ctx iris.Context
}

// 启动一个 iris 应用
func newApp() *iris.Application {
	app := iris.New()
	mvc.New(app.Party("/")).Handle(&lotteryController{})

	return app
}

func main() {
	app := newApp()
	err := app.Listen(":8080")

	if err != nil {
		panic(fmt.Sprintf("web server start error : %s\n", err))
	}
}

// Get http://localhost:8080
func (c *lotteryController) Get() map[uint32][2]int {
	// 返回当前全部得红包
	rs := make(map[uint32][2]int)
	packageList.Range(func(key, value interface{}) bool {
		id := key.(uint32)
		list := value.([]uint)
		var money int
		for _, v := range list {
			money += int(v)
		}
		rs[id] = [2]int{len(list), money}
		return true
	})

	return rs
}

// GetSet   http://localhost:8080/set?uid=1&money=100&num=100
func (c *lotteryController) GetSet() string {
	uid, errUid := c.Ctx.URLParamInt("uid")
	moeny, errMoney := c.Ctx.URLParamFloat64("money")
	num, errNum := c.Ctx.URLParamInt("num")

	if errUid != nil || errNum != nil || errMoney != nil {
		fmt.Sprintf("errUid=%d, errMoney=%d, errNum=%d \n", errUid, errMoney, errNum)
	}
	moenyTotal := int(moeny * 100)
	if uid < 1 || moenyTotal < num || num < 1 {
		return fmt.Sprintf("参数数值异常, uid=%d, money=%d, num=%d \n", uid, moeny, num)
	}
	// 金额分配算法
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	rMax := 0.55 // 随机分配最大值
	if num > 1000 {
		rMax = 0.01
	} else if num < 10 {
		rMax = 0.80
	}
	list := make([]uint, num)
	leftMoney := moenyTotal
	leftNum := num

	for leftNum > 0 {
		if leftNum == 1 {
			list[num-1] = uint(leftMoney)
			break
		}
		// 剩余钱数等于剩余红包数每个红包进行均分
		if leftMoney == leftNum {
			for i := num - leftNum; i < num; i++ {
				list[i] = 1
				break
			}
		}
		// 随机分配最大值
		rMoney := int(float64(leftMoney-leftNum) * rMax)
		m := r.Intn(rMoney)
		if m < 1 {
			m = 1
		}
		list[num-leftNum] = uint(m)
		leftMoney -= m
		leftNum--

	}
	// 红包得UUID
	id := r.Uint32()

	packageList.Store(id, list)
	return fmt.Sprintf("/get?id=%d&uid=%d&num=%d", id, uid, num)
}

// GetGet http://localhost:8080/get?id=1&uid=1
func (c *lotteryController) GetGet() string {
	id, errid := c.Ctx.URLParamInt("id")
	uid, errUid := c.Ctx.URLParamInt("uid")

	if errUid != nil || errid != nil {
		return fmt.Sprintf("")
	}
	if uid < 1 || id < 1 {
		return fmt.Sprintf("")
	}
	listq, ok := packageList.Load(uint32(id))
	list := listq.([]int)
	if !ok || len(list) < 1 {
		return fmt.Sprintf("红包不存在, id =%d \n", id)
	}
	// 分配随机数获取红包
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	i := r.Intn(len(list))
	money := list[i]

	// 更新红包中列表信息
	if len(list) > 1 {
		if i == len(list)-1 {
			packageList.Store(uint32(id), list[:i])
		} else if i == 0 {

			packageList.Store(uint32(id), list[1:])
		} else {

			packageList.Store(uint32(id), append(list[:i], list[i+1:]...))
		}
	} else {
		packageList.Delete(uint32(id))

	}
	return fmt.Sprintf("恭喜你抢到一个红包， 红包金额：%d \n", money)
}
